Senegalese tax and benefit system from scratch


In [1]:
%load_ext autoreload
%autoreload 2

In [2]:
import numpy as np

from openfisca_core.model_api import *
from openfisca_senegal import SenegalTaxBenefitSystem
from openfisca_senegal.entities import Individu


libyaml is not installed in your environment. This can make OpenFisca slower to start. Once you have installed libyaml, run 'pip uninstall pyyaml && pip install pyyaml --no-cache-dir' so that it is used in your Python environment.


In [3]:
tax_benefit_system = SenegalTaxBenefitSystem()

Les paramètres de la législation sont définis ci-dessous via une string XML.

Le barème de l'impôt progressif a été récupéré dans le fichier http://www.gouv.sn/IMG/pdf/cgi2013.pdf à la page 71.


In [4]:
tax_benefit_system.add_legislation_params(u'''
<NODE code="root">
  <BAREME code="bareme_impot_progressif" type="monetary">
    <TRANCHE code="tranche0">
      <SEUIL>
        <VALUE deb="2013-01-01" valeur="0" />
      </SEUIL>
      <TAUX>
        <VALUE deb="2013-01-01" valeur="0" />
      </TAUX>
    </TRANCHE>
    <TRANCHE code="tranche1">
      <SEUIL>
        <VALUE deb="2013-01-01" valeur="630000" />
      </SEUIL>
      <TAUX>
        <VALUE deb="2013-01-01"  valeur="0.2" />
      </TAUX>
    </TRANCHE>
    <TRANCHE code="tranche2">
      <SEUIL>
        <VALUE deb="2013-01-01"  valeur="1500000" />
      </SEUIL>
      <TAUX>
        <VALUE deb="2013-01-01"  valeur="0.3" />
      </TAUX>
    </TRANCHE>
    <TRANCHE code="tranche3">
      <SEUIL>
        <VALUE deb="2013-01-01"  valeur="4000000" />
      </SEUIL>
      <TAUX>
        <VALUE deb="2013-01-01"  valeur="0.35" />
      </TAUX>
    </TRANCHE>
    <TRANCHE code="tranche4">
      <SEUIL>
        <VALUE deb="2013-01-01"  valeur="8000000" />
      </SEUIL>
      <TAUX>
        <VALUE deb="2013-01-01"  valeur="0.37" />
      </TAUX>
    </TRANCHE>
    <TRANCHE code="tranche5">
      <SEUIL>
        <VALUE deb="2013-01-01"  valeur="13500000" />
      </SEUIL>
      <TAUX>
        <VALUE deb="2013-01-01"  valeur="0.4" />
      </TAUX>
    </TRANCHE>
  </BAREME>
  <NODE code="reductions_pour_charge_de_famille">
    <CODE code="taux_1" format="percent">
      <VALUE deb="2013-01-01"  valeur="0" />
    </CODE>
    <CODE code="taux_2" format="percent">
      <VALUE deb="2013-01-01"  valeur="0.1" />
    </CODE>
    <CODE code="taux_3" format="percent">
      <VALUE deb="2013-01-01"  valeur="0.15" />
    </CODE>
    <CODE code="taux_4" format="percent">
      <VALUE deb="2013-01-01"  valeur="0.2" />
    </CODE>
    <CODE code="taux_5" format="percent">
      <VALUE deb="2013-01-01"  valeur="0.25" />
    </CODE>
    <CODE code="taux_6" format="percent">
      <VALUE deb="2013-01-01"  valeur="0.3" />
    </CODE>
    <CODE code="taux_7" format="percent">
      <VALUE deb="2013-01-01"  valeur="0.35" />
    </CODE>
    <CODE code="taux_8" format="percent">
      <VALUE deb="2013-01-01"  valeur="0.4" />
    </CODE>
    <CODE code="taux_9" format="percent">
      <VALUE deb="2013-01-01"  valeur="0.45" />
    </CODE>
    <CODE code="min_1" type="monetary">
      <VALUE deb="2013-01-01"  valeur="0" />
    </CODE>
    <CODE code="min_2" type="monetary">
      <VALUE deb="2013-01-01"  valeur="100000" />
    </CODE>
    <CODE code="min_3" type="monetary">
      <VALUE deb="2013-01-01"  valeur="200000" />
    </CODE>
    <CODE code="min_4" type="monetary">
      <VALUE deb="2013-01-01"  valeur="300000" />
    </CODE>
    <CODE code="min_5" type="monetary">
      <VALUE deb="2013-01-01"  valeur="400000" />
    </CODE>
    <CODE code="min_6" type="monetary">
      <VALUE deb="2013-01-01"  valeur="500000" />
    </CODE>
    <CODE code="min_7" type="monetary">
      <VALUE deb="2013-01-01"  valeur="600000" />
    </CODE>
    <CODE code="min_8" type="monetary">
      <VALUE deb="2013-01-01"  valeur="700000" />
    </CODE>
    <CODE code="min_9" type="monetary">
      <VALUE deb="2013-01-01"  valeur="800000" />
    </CODE>
    <CODE code="max_1" type="monetary">
      <VALUE deb="2013-01-01"  valeur="0" />
    </CODE>
    <CODE code="max_2" type="monetary">
      <VALUE deb="2013-01-01"  valeur="300000" />
    </CODE>
    <CODE code="max_3" type="monetary">
      <VALUE deb="2013-01-01"  valeur="650000" />
    </CODE>
    <CODE code="max_4" type="monetary">
      <VALUE deb="2013-01-01"  valeur="1100000" />
    </CODE>
    <CODE code="max_5" type="monetary">
      <VALUE deb="2013-01-01"  valeur="1650000" />
    </CODE>
    <CODE code="max_6" type="monetary">
      <VALUE deb="2013-01-01"  valeur="2030000" />
    </CODE>
    <CODE code="max_7" type="monetary">
      <VALUE deb="2013-01-01"  valeur="2490000" />
    </CODE>
    <CODE code="max_8" type="monetary">
      <VALUE deb="2013-01-01"  valeur="2755000" />
    </CODE>
    <CODE code="max_9" type="monetary">
      <VALUE deb="2013-01-01"  valeur="3180000" />
    </CODE>
  </NODE>
</NODE>
''')


---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-4-7d97bedaecc7> in <module>()
----> 1 tax_benefit_system.add_legislation_params(u'''
      2 <NODE code="root">
      3   <BAREME code="bareme_impot_progressif" type="monetary">
      4     <TRANCHE code="tranche0">
      5       <SEUIL>

AttributeError: 'SenegalTaxBenefitSystem' object has no attribute 'add_legislation_params'

In [ ]:
class date_de_naissance(Variable):
    value_type = date
    definition_period = ETERNITY
    entity = Individu
    label = u"Date de naissance"

    
tax_benefit_system.update_variable(date_de_naissance)


class salaire(Variable):
    value_type = float
    definition_period = YEAR
    entity = Individu
    label = "Salaire"
    set_input = set_input_divide_by_period

tax_benefit_system.update_variable(salaire)

In [ ]:
class est_marie(Variable):
    value_type = bool
    definition_period = YEAR
    entity = Individu
    label = u"Est marié"
    set_input = set_input_dispatch_by_period

tax_benefit_system.update_variable(est_marie)


class conjoint_a_des_revenus(Variable):
    value_type = bool
    definition_period = YEAR
    entity = Individu

tax_benefit_system.update_variable(conjoint_a_des_revenus)


class nombre_enfants(Variable):
    value_type = int
    definition_period = YEAR
    entity = Individu

tax_benefit_system.update_variable(nombre_enfants)

In [ ]:
class nombre_de_parts(Variable):
    value_type = float
    definition_period = YEAR
    entity = Individu
    label = u"Nombre de parts"

    def formula(individu, period):
        nombre_de_parts_enfants = individu('nombre_enfants', period) * 0.5

        conjoint_a_des_revenus = individu('conjoint_a_des_revenus', period)
        est_marie = individu('est_marie', period)
        nombre_de_parts_conjoint = est_marie * 0.5 + (1 - conjoint_a_des_revenus) * 0.5

        nombre_de_parts = 1 + nombre_de_parts_conjoint + nombre_de_parts_enfants

        return np.minimum(5, nombre_de_parts)
        

tax_benefit_system.update_variable(nombre_de_parts)


class impot_avant_reduction_famille(Variable):
    value_type = float
    definition_period = YEAR
    entity = Individu

    def formula(individu, period, legislation):
        salaire = individu('salaire', period, options = [ADD])
        bareme_impot_progressif = legislation(period).bareme_impot_progressif
        return bareme_impot_progressif.calc(salaire)

tax_benefit_system.update_variable(impot_avant_reduction_famille)


class reduction_impots_pour_charge_famille(Variable):
    value_type = float
    definition_period = YEAR
    entity = Individu

    def formula(individu, period, legislation):
        impot_avant_reduction_famille = individu('impot_avant_reduction_famille', period)
        
        nombre_de_parts = individu('nombre_de_parts', period)
        reductions_pour_charge_de_famille = legislation(period).reductions_pour_charge_de_famille
        taux = (nombre_de_parts == 1) * reductions_pour_charge_de_famille.taux_1 + \
            (nombre_de_parts == 1.5) * reductions_pour_charge_de_famille.taux_2 + \
            (nombre_de_parts == 2) * reductions_pour_charge_de_famille.taux_3 + \
            (nombre_de_parts == 2.5) * reductions_pour_charge_de_famille.taux_4 + \
            (nombre_de_parts == 3) * reductions_pour_charge_de_famille.taux_5 + \
            (nombre_de_parts == 3.5) * reductions_pour_charge_de_famille.taux_6 + \
            (nombre_de_parts == 4) * reductions_pour_charge_de_famille.taux_7 + \
            (nombre_de_parts == 4.5) * reductions_pour_charge_de_famille.taux_8 + \
            (nombre_de_parts == 5) * reductions_pour_charge_de_famille.taux_9
        minimum = (nombre_de_parts == 1) * reductions_pour_charge_de_famille.min_1 + \
            (nombre_de_parts == 1.5) * reductions_pour_charge_de_famille.min_2 + \
            (nombre_de_parts == 2) * reductions_pour_charge_de_famille.min_3 + \
            (nombre_de_parts == 2.5) * reductions_pour_charge_de_famille.min_4 + \
            (nombre_de_parts == 3) * reductions_pour_charge_de_famille.min_5 + \
            (nombre_de_parts == 3.5) * reductions_pour_charge_de_famille.min_6 + \
            (nombre_de_parts == 4) * reductions_pour_charge_de_famille.min_7 + \
            (nombre_de_parts == 4.5) * reductions_pour_charge_de_famille.min_8 + \
            (nombre_de_parts == 5) * reductions_pour_charge_de_famille.min_9
        maximum = (nombre_de_parts == 1) * reductions_pour_charge_de_famille.max_1 + \
            (nombre_de_parts == 1.5) * reductions_pour_charge_de_famille.max_2 + \
            (nombre_de_parts == 2) * reductions_pour_charge_de_famille.max_3 + \
            (nombre_de_parts == 2.5) * reductions_pour_charge_de_famille.max_4 + \
            (nombre_de_parts == 3) * reductions_pour_charge_de_famille.max_5 + \
            (nombre_de_parts == 3.5) * reductions_pour_charge_de_famille.max_6 + \
            (nombre_de_parts == 4) * reductions_pour_charge_de_famille.max_7 + \
            (nombre_de_parts == 4.5) * reductions_pour_charge_de_famille.max_8 + \
            (nombre_de_parts == 5) * reductions_pour_charge_de_famille.max_9
        reduction_impot = np.clip(impot_avant_reduction_famille * taux, a_min=minimum, a_max=maximum)
        return reduction_impot

tax_benefit_system.update_variable(reduction_impots_pour_charge_famille)


class impot_revenus(Variable):
    value_type = float
    definition_period = YEAR
    entity = Individu

    def formula(individu, period):
        impot_avant_reduction_famille = individu('impot_avant_reduction_famille', period)
        reduction_impots_pour_charge_famille = individu('reduction_impots_pour_charge_famille', period)
        impot_apres_reduction_famille = impot_avant_reduction_famille - reduction_impots_pour_charge_famille
        return np.maximum(0, impot_apres_reduction_famille)

tax_benefit_system.update_variable(impot_revenus)

In [ ]:
scenario = tax_benefit_system.new_scenario()

In [ ]:
scenario.init_single_entity(
    parent1={
        'salaire': 1800000,
        'est_marie': True,
        'conjoint_a_des_revenus': False,
        'nombre_enfants': 2,
    },
    period='2015',
)

In [ ]:
simulation = scenario.new_simulation()

In [ ]:
simulation.individu('salaire', period='2015')

In [ ]:
simulation.individu('impot_avant_reduction_famille', period='2015')

In [ ]:
simulation.individu('reduction_impots_pour_charge_famille', period='2015')

In [ ]:
simulation.individu('impot_revenus', period='2015')

In [ ]:
(1500000 - 630000) * 0.2 + (1800000 - 1500000) * 0.3

In [ ]:
simulation.individu('nombre_de_parts', period='2015')

Simuler plusieurs cas types à la fois


In [ ]:
import matplotlib.pyplot as plt
%matplotlib inline

In [ ]:
scenario1 = tax_benefit_system.new_scenario()
scenario1.init_single_entity(
    parent1={
        'est_marie': True,
        'conjoint_a_des_revenus': False,
        'nombre_enfants': 0,
    },
    period='2015',
    axes=[
        {
            'count': 100,
            'min': 0,
            'max': 15e6,
            'name': 'salaire',
        }
    ],
)
simulation1 = scenario1.new_simulation()

salaire1 = simulation1.individu('salaire', period='2015')
reduction_impots_pour_charge_famille1 = simulation1.individu('reduction_impots_pour_charge_famille', period='2015')
impot_avant_reduction_famille1 = simulation1.individu('impot_avant_reduction_famille', period='2015')
impot_revenus1 = simulation1.individu('impot_revenus', period='2015')

In [ ]:
plt.figure(figsize=(12, 8))
plt.plot(salaire1, impot_avant_reduction_famille1, label=u'avant réduction famille')
plt.plot(salaire1, reduction_impots_pour_charge_famille1, label=u'réduction famille')
plt.plot(salaire1, impot_revenus1, label=u'impôt revenus')
plt.xlabel(u'Salaire')
plt.legend()
plt.title(u'0 enfants', fontsize=20)

In [ ]:
scenario2 = tax_benefit_system.new_scenario()
scenario2.init_single_entity(
    parent1={
        'est_marie': True,
        'conjoint_a_des_revenus': False,
        'nombre_enfants': 1,
    },
    period='2015',
    axes=[
        {
            'count': 100,
            'min': 0,
            'max': 15e6,
            'name': 'salaire',
        }
    ],
)
simulation2 = scenario2.new_simulation()

salaire2 = simulation2.individu('salaire', period='2015')
reduction_impots_pour_charge_famille2 = simulation2.individu('reduction_impots_pour_charge_famille', period='2015')
impot_avant_reduction_famille2 = simulation2.individu('impot_avant_reduction_famille', period='2015')
impot_revenus2 = simulation2.individu('impot_revenus', period='2015')

In [ ]:
plt.figure(figsize=(12, 8))
plt.plot(salaire2, impot_avant_reduction_famille2, label=u'avant réduction famille')
plt.plot(salaire2, reduction_impots_pour_charge_famille2, label=u'réduction famille')
plt.plot(salaire2, impot_revenus2, label=u'impôt revenus')
plt.xlabel(u'Salaire')
plt.legend()
plt.title(u'1 enfant', fontsize=20)

In [ ]:
impot_revenus_diff = np.abs(impot_revenus2 - impot_revenus1)
plt.figure(figsize=(12, 8))
plt.plot(salaire1, impot_revenus_diff, label=u'Différence d\'impôt revenus')
plt.xlabel(u'Salaire')
plt.legend()
plt.title(u'Gain fiscal du 1er enfant', fontsize=20)

plot_margin = 10000
x0, x1, y0, y1 = plt.axis()
plt.axis((x0 - plot_margin, x1 + plot_margin, y0 - plot_margin, y1 + plot_margin))

Affichage des taux marginaux


In [ ]:
from openfisca_core import rates

In [ ]:
fig, ax1 = plt.subplots(figsize=(12, 8))

ax1.plot(salaire1, impot_revenus1)
ax1.set_xlabel(u'Salaire')
ax1.set_ylabel(u'Impôt sur les revenus')

ax2 = ax1.twinx()
ax2.set_ylabel(u'Taux marginaux')
ax2.plot(
    salaire1[:-1],
    1 - rates.marginal_rate(target=impot_revenus1, varying=salaire1),
)

In [ ]: